﻿Imports Microsoft.WindowsMobile.Status
Imports Microsoft.WindowsMobile.Status.SystemProperty

Public Class EventManager
  Private _lastPhoneInUse As Boolean = False

  Private _ini As Ini.File = Nothing
  Private _flags As New Dictionary(Of Char, Integer)

  Private WithEvents _incomingCall As New SystemState(PhoneIncomingCall, True)
  Private WithEvents _activeCall As New SystemState(PhoneCallTalking, True)
  Private WithEvents _outgoingCall As New SystemState(PhoneCallCalling, True)

  Public Event GetInList(ByRef inList As Boolean)
  Public Event GetCurrentPlayState(ByRef curState As ComparisonPlayStates)
  Public Event GetCurrentItemType(ByRef curType As ComparisonItemTypes)
  Public Event ExecuteControlFunction(ByVal func As Integer, ByVal arg As Integer, ByVal pressType As ButtonActions, ByRef handled As Boolean)

  Public Enum ComparisonPlayStates As Integer
    Stopped = 0
    Playing = 1
    Paused = 2
  End Enum

  Public Enum ComparisonItemTypes As Integer
    None = 0
    File = 1
    Stream = 2
  End Enum

  Public Enum ButtonActions As Integer
    Down = 0
    Up = 1
    Press = 2
  End Enum


  Private Sub AnyCall_Changed() Handles _incomingCall.Changed, _activeCall.Changed, _outgoingCall.Changed
    Me.ExecutePhoneInUseEvent()
  End Sub

  Public Function IsPhoneInUse() As Boolean
    Return SystemState.PhoneIncomingCall OrElse SystemState.PhoneCallTalking OrElse SystemState.PhoneCallCalling
  End Function

  Private Sub ExecutePhoneInUseEvent()
    Dim curPhoneInUse As Boolean = Me.IsPhoneInUse

    '    WriteToErrorLog("----PhoneInUse-LAST-" & Me._lastPhoneInUse & "-CUR-" & curPhoneInUse)

    If Me._lastPhoneInUse <> curPhoneInUse Then
      If curPhoneInUse Then
        Me.OnPhoneInUseStart()
      Else
        Me.OnPhoneInUseEnd()
      End If
    End If
    Me._lastPhoneInUse = curPhoneInUse
  End Sub


  Public Sub New()
    Me._lastPhoneInUse = Me.IsPhoneInUse()
    Me.ReloadEventsINI()
  End Sub

  Private Sub ResetFlags()
    Me._flags.Clear()
    Me._flags.Add("A"c, 0)
    Me._flags.Add("B"c, 0)
    Me._flags.Add("C"c, 0)
    Me._flags.Add("D"c, 0)
    Me._flags.Add("E"c, 0)
    Me._flags.Add("F"c, 0)
    Me._flags.Add("G"c, 0)
    Me._flags.Add("H"c, 0)
    Me._flags.Add("I"c, 0)
    Me._flags.Add("J"c, 0)
    Me._flags.Add("K"c, 0)
    Me._flags.Add("L"c, 0)
    Me._flags.Add("M"c, 0)
    Me._flags.Add("N"c, 0)
    Me._flags.Add("O"c, 0)
    Me._flags.Add("P"c, 0)
    Me._flags.Add("Q"c, 0)
    Me._flags.Add("R"c, 0)
    Me._flags.Add("S"c, 0)
    Me._flags.Add("T"c, 0)
    Me._flags.Add("U"c, 0)
    Me._flags.Add("V"c, 0)
    Me._flags.Add("W"c, 0)
    Me._flags.Add("X"c, 0)
    Me._flags.Add("Y"c, 0)
    Me._flags.Add("Z"c, 0)
  End Sub

  Public Sub ReloadEventsINI()
    Me.ResetFlags()
    Me._ini = New Ini.File(Settings.INIEventsPath)
    Me._lastPhoneInUse = Me.IsPhoneInUse
  End Sub



  Private Function ProcessEvent_Conditional(ByVal str As String, ByVal pressType As ButtonActions) As Boolean
    ProcessEvent_Conditional = False
    Try
      ' Split at the "then"
      Dim strs() As String = str.Split(";"c)
      If strs.Length = 2 Then
        ' Parse question
        Dim success As Boolean = False
        Dim answer As Boolean = False

        With System.Text.RegularExpressions.Regex.Match(strs(0), "[?]F(?<F>[A-Z])\s*\=\s*(?<V>\d+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
          ' Captures flag in <F> and value in <V>
          If .Success Then
            success = True

            Try
              If (Me._flags(.Groups("F").Captures(0).Value.Trim.ToUpper()(0)) = Integer.Parse(.Groups("V").Value)) Then
                answer = True
                '                WriteToErrorLog("----IF(" & .Groups("F").Captures(0).Value.Trim.ToUpper()(0) & "--" & Integer.Parse(.Groups("V").Value))
              End If
            Catch ex As Exception
              ErrorLog.WriteToErrorLog("ProcessEvent :: Flag conditional comparison error..." & vbCrLf & "  " & strs(0) & vbCrLf & vbCrLf & ex.ToString)
            End Try
          End If
        End With

        If Not success Then
          With System.Text.RegularExpressions.Regex.Match(strs(0), "[?]PS\s*\=\s*(?<V>PL|PA|ST)", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
            ' PL, PA, or ST - the valid play states
            If .Success Then
              success = True

              Try
                Dim ps As ComparisonPlayStates = ComparisonPlayStates.Stopped
                RaiseEvent GetCurrentPlayState(ps)

                Select Case .Groups("V").Captures(0).Value.Trim.ToUpper
                  Case "PL"
                    If ps = ComparisonPlayStates.Playing Then
                      '                      WriteToErrorLog("----IF(PS--PL)")
                      answer = True
                    End If
                  Case "PA"
                    If ps = ComparisonPlayStates.Paused Then
                      '                      WriteToErrorLog("----IF(PS--PA)")
                      answer = True
                    End If
                  Case "ST"
                    If ps = ComparisonPlayStates.Stopped Then
                      '                      WriteToErrorLog("----IF(PS--ST)")
                      answer = True
                    End If
                  Case Else
                    ErrorLog.WriteToErrorLog("ProcessEvent :: PlayState invalid conditional comparison value..." & vbCrLf & "  " & strs(0))
                End Select
              Catch ex As Exception
                ErrorLog.WriteToErrorLog("ProcessEvent :: Playstate conditional comparison error..." & vbCrLf & "  " & strs(0) & vbCrLf & vbCrLf & ex.ToString)
              End Try
            End If
          End With
        End If

        If Not success Then
          With System.Text.RegularExpressions.Regex.Match(strs(0), "[?]IT\s*\=\s*(?<V>FI|ST|NO)", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
            ' FI, ST, or NO - the valid item types
            If .Success Then
              success = True

              Try
                Dim it As ComparisonItemTypes = ComparisonItemTypes.None
                RaiseEvent GetCurrentItemType(it)

                Select Case .Groups("V").Captures(0).Value.Trim.ToUpper
                  Case "FI"
                    If it = ComparisonItemTypes.File Then
                      answer = True
                    End If
                  Case "ST"
                    If it = ComparisonItemTypes.Stream Then
                      answer = True
                    End If
                  Case "NO"
                    If it = ComparisonItemTypes.None Then
                      answer = True
                    End If
                  Case Else
                    ErrorLog.WriteToErrorLog("ProcessEvent :: File type invalid conditional comparison value..." & vbCrLf & "  " & strs(0))
                End Select
              Catch ex As Exception
                ErrorLog.WriteToErrorLog("ProcessEvent :: File type conditional comparison error..." & vbCrLf & "  " & strs(0) & vbCrLf & vbCrLf & ex.ToString)
              End Try
            End If
          End With
        End If

        If success Then
          ' If the quesiton returned a TRUE
          If answer Then
            Dim astr As String = strs(1).Trim
            If astr(0) = "!" Then
              Me.ProcessEvent_Assignment(astr)
              ProcessEvent_Conditional = True
            ElseIf astr(0) = "#" Then
              ProcessEvent_Conditional = Me.ProcessEvent_Action(astr, pressType)
            Else
              ErrorLog.WriteToErrorLog("ProcessEvent :: Unknown command type after conditional expression..." & vbCrLf & "  " & str)
            End If
          End If
        Else ' No regex match
          ErrorLog.WriteToErrorLog("ProcessEvent :: Invalid conditional expression..." & vbCrLf & "  " & str)
        End If
      Else ' length is too small
        ErrorLog.WriteToErrorLog("ProcessEvent :: Conditional expression is too short..." & vbCrLf & "  " & str)
      End If
    Catch ex As Exception
      ErrorLog.WriteToErrorLog("ProcessEvent :: Exception while processing conditional..." & vbCrLf & "  " & str & vbCrLf & vbCrLf & ex.ToString)
    End Try
  End Function

  Private Sub ProcessEvent_Assignment(ByVal str As String)
    Try
      ' Captures flag in <F> and value in <V>
      Dim r As New System.Text.RegularExpressions.Regex("[!]F(?<F>[A-Z])\s*\=\s*(?<V>\d+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
      With r.Match(str)
        If .Success Then
          If .Groups("F") IsNot Nothing AndAlso .Groups("V") IsNot Nothing AndAlso .Groups("F").Captures.Count > 0 AndAlso .Groups("V").Captures.Count > 0 Then
            '            WriteToErrorLog("----ASSIGN(" & .Groups("F").Captures(0).Value.Trim.ToUpper()(0) & "--" & Integer.Parse(.Groups("V").Value))
            Me._flags(.Groups("F").Captures(0).Value.Trim.ToUpper()(0)) = Integer.Parse(.Groups("V").Captures(0).Value)
          End If
        Else
          ErrorLog.WriteToErrorLog("ProcessEvent :: Unsuccessful regex match in assignment..." & vbCrLf & "  " & str)
        End If
      End With
    Catch ex As Exception
      ErrorLog.WriteToErrorLog("ProcessEvent :: Exception while processing assignment..." & vbCrLf & "  " & str & vbCrLf & vbCrLf & ex.ToString)
    End Try
  End Sub

  Private Function ProcessEvent_Action(ByVal str As String, ByVal pressType As ButtonActions) As Boolean
    Try
      If str.Length > 1 Then
        If str(0) = "#"c Then
          str = str.Substring(1)
        End If

        ' Find all possible commands
        For Each s As String In str.Split(","c)
          Dim func As Integer = 0
          Dim arg As Integer = 0
          Dim handled As Boolean = False

          ' See if we have an argument
          If s.Contains(":") Then
            Dim funcs = Left(s, s.IndexOf(":"))
            Dim args = Mid(s, s.IndexOf(":") + 2)

            func = Integer.Parse(funcs)
            arg = Integer.Parse(args)
          Else
            func = Integer.Parse(s)
          End If

          If Not handled Then
            '            WriteToErrorLog("----CONTROL(" & func & "-" & arg & ")")
            RaiseEvent ExecuteControlFunction(func, arg, pressType, handled)
          End If

          ' handled can be reassigned in the ExecuteControlFunction, so don't combine these two "if" statements
          If handled Then
            Return True
          End If
        Next
      End If
    Catch ex As Exception
      ErrorLog.WriteToErrorLog("ProcessEvent :: Exception while processing action..." & vbCrLf & "  " & str & vbCrLf & vbCrLf & ex.ToString)
    End Try

    Return False
  End Function


  Private Function ProcessEvent(ByVal section As String, ByVal pressType As ButtonActions, Optional ByVal checkInList As Boolean = False, Optional ByVal repeatingPress As Boolean = False) As Boolean
    ProcessEvent = False

    ' Check to see if we need to load the INI
    If Me._ini Is Nothing Then Me.ReloadEventsINI()

    ' Find the INI section, based on the event
    Dim inis As Ini.Section = Me._ini.Section(section)
    If inis IsNot Nothing Then
      '      ErrorLog.WriteToErrorLog("----EVENT-SECTION-HAPPY--" & section)

      If checkInList Then
        Dim inList As Boolean = False
        RaiseEvent GetInList(inList)

        If inList AndAlso Not inis.BooleanValueNoNull("ActiveOnList") Then
          '          ErrorLog.WriteToErrorLog("----EVENT-IN-LIST-EXIT")
          ' We are allowed to check if we are in a list, are in a list, and are not allowed to process while in a list
          Return False
        End If
      End If

      If repeatingPress AndAlso Not inis.BooleanValueNoNull("AllowRepeat") Then
        Return True
      End If

      ' We found the event, parse the script
      For Each inik As String In inis.Keys
        '        ErrorLog.WriteToErrorLog("----INIKEY-" & inik)

        If Left(inik, 6).ToUpper = "ACTION" Then
          Dim line As String = inis.StringValueNoNull(inik).Trim.ToUpper
          Select Case line(0)
            Case "?"c
              '              ErrorLog.WriteToErrorLog("----EVENT-CONDITIONAL-")
              ProcessEvent = ProcessEvent Or Me.ProcessEvent_Conditional(line, pressType)
            Case "!"c
              '              ErrorLog.WriteToErrorLog("----EVENT-ASSIGNMENT-")
              Me.ProcessEvent_Assignment(line)
              ProcessEvent = True
            Case "#"c ' Execute control function
              '              ErrorLog.WriteToErrorLog("----EVENT-EXECUTE-")
              ProcessEvent = ProcessEvent Or Me.ProcessEvent_Action(line, pressType)
          End Select
        End If
      Next
    Else
      '      ErrorLog.WriteToErrorLog("----EVENT-SECTION-NOT-FOUND--" & section)
    End If
  End Function




  ' --------------------------------- The following events are internally activated --------------------------------- 

  ''' <summary>
  ''' Executed anytime the phone subsystem begins activity (ringing or in-call).  Between ringing end and phone in-call, there is no
  ''' execution of OnPhoneInUseEnd's events -- there is a seamless transition.
  ''' </summary>
  Private Sub OnPhoneInUseStart()
    Me.ProcessEvent("OnPhoneInUseStart", ButtonActions.Press)
  End Sub

  ''' <summary>
  ''' Executed anytime the phone subsystem is active (ringing or in-call).  Between ringing end and phone in-call, there is no
  ''' execution of OnPhoneInUseEnd's events -- there is a seamless transition.
  ''' </summary>
  Private Sub OnPhoneInUseEnd()
    Me.ProcessEvent("OnPhoneInUseEnd", ButtonActions.Press)
  End Sub



  ' --------------------------------- The following events are externally activated --------------------------------- 

  ''' <summary>
  ''' Executed after the application is initialized and items loaded, but before automatic playback
  ''' </summary>
  Public Sub OnApplicationStart()
    Me.ProcessEvent("OnApplicationStart", ButtonActions.Press)
  End Sub

  ''' <summary>
  ''' Executed when the application is about to terminate, but before anything is uninitialized
  ''' </summary>
  Public Sub OnApplicationExit()
    Me.ProcessEvent("OnApplicationExit", ButtonActions.Press)
  End Sub

  ''' <summary>
  ''' Executed on KeyDown, when the control surface is visible (lists do not activate this event)
  ''' </summary>
  ''' <param name="keycode">The numeric key code representing the key press</param>
  ''' <returns>Whether the key is handled or not.</returns>
  Public Function OnKeyDown(ByVal keycode As Integer, Optional ByVal repeatingPress As Boolean = False) As Boolean
    ' Convert the keycode to hex for the event processing and also tell the event processor to check if we are in a list
    Return Me.ProcessEvent("Key_" & keycode.ToString("X"), ButtonActions.Down, True, repeatingPress)
  End Function

  ''' <summary>
  ''' Executed on KeyUp, when the control surface is visible (lists do not activate this event)
  ''' </summary>
  ''' <param name="keycode">The numeric key code representing the key press</param>
  ''' <returns>Whether the key is handled or not.</returns>
  Public Function OnKeyUp(ByVal keycode As Integer) As Boolean
    ' Convert the keycode to hex for the event processing and also tell the event processor to check if we are in a list
    Return Me.ProcessEvent("Key_" & keycode.ToString("X"), ButtonActions.Up, True)
  End Function
End Class
